/*
 * Copyright (C) 2021 The Chinese Software International Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.tonyodev.fetch2.provider;

import ohos.app.Context;
import ohos.event.commonevent.CommonEventData;
import ohos.event.commonevent.CommonEventSubscribeInfo;
import ohos.event.commonevent.CommonEventSubscriber;
import ohos.event.commonevent.CommonEventSupport;
import ohos.event.commonevent.MatchingSkills;
import ohos.net.NetManager;
import ohos.net.NetSpecifier;
import ohos.net.NetStatusCallback;
import ohos.rpc.RemoteException;
import ohos.sysappcomponents.settings.SystemSettings;

import com.tonyodev.fetch2.NetworkType;
import com.tonyodev.fetch2core.FetchExtensions;

import ohos.event.commonevent.CommonEventManager;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.Iterator;

import static com.tonyodev.fetch2.FetchIntent.CONNECTIVITY_ACTION;

/**
 * description NetworkInfoProvider
 *
 * @author liuluming
 * @since 2021-05-24
 */
public class NetworkInfoProvider {
    private MatchingSkills skills;
    private Object lock;
    private HashSet networkChangeListenerSet;
    private NetManager connectivityManager;
    private MainEventSubscriber networkChangeBroadcastReceiver;
    private boolean broadcastRegistered;
    private Object networkCallback;
    private Context context;
    private String internetCheckUrl;

    private void notifyNetworkChangeListeners() {
        synchronized (lock) {
            Iterator iterator = this.networkChangeListenerSet.iterator();
            while (iterator.hasNext()) {
                Object element = iterator.next();
                NetworkInfoProvider.NetworkChangeListener listener = (NetworkInfoProvider.NetworkChangeListener) element;
                listener.onNetworkChanged();
            }
        }
    }

    /**
     * registerNetworkChangeListener
     *
     * @param networkChangeListener networkChangeListener
     */
    public void registerNetworkChangeListener(@NotNull NetworkInfoProvider.NetworkChangeListener networkChangeListener) {
        synchronized (lock) {
            this.networkChangeListenerSet.add(networkChangeListener);
        }
    }

    /**
     * unregisterNetworkChangeListener
     *
     * @param networkChangeListener networkChangeListener
     */
    public void unregisterNetworkChangeListener(@NotNull NetworkInfoProvider.NetworkChangeListener networkChangeListener) {
        synchronized (lock) {
            this.networkChangeListenerSet.remove(networkChangeListener);
        }
    }

    public void unregisterAllNetworkChangeListeners() {
        synchronized (lock) {
            this.networkChangeListenerSet.clear();
            if (this.broadcastRegistered) {
                try {
                    CommonEventManager.unsubscribeCommonEvent(this.networkChangeBroadcastReceiver);
                } catch (Exception e) {
                }
            }

            if (this.connectivityManager != null) {
                Object networkCallback = this.networkCallback;
                if (networkCallback instanceof NetManager) {
                    try {
                        CommonEventManager.unsubscribeCommonEvent(this.networkChangeBroadcastReceiver);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * isOnAllowedNetwork
     *
     * @param networkType networkType
     * @return true
     */
    public boolean isOnAllowedNetwork(@NotNull NetworkType networkType) {
        if (networkType == NetworkType.WIFI_ONLY && FetchExtensions.isOnWiFi(this.context)) {
            return true;
        } else if (networkType == NetworkType.UNMETERED && !FetchExtensions.isOnMeteredConnection(this.context)) {
            return true;
        } else {
            return networkType == NetworkType.ALL && FetchExtensions.isNetworkAvailable(this.context);
        }
    }

    /**
     * isNetworkAvailable
     *
     * @return true
     */
    public boolean isNetworkAvailable() {
        String url = this.internetCheckUrl;
        boolean isNetwork;
        if (url != null) {
            boolean connected = false;

            try {
                URL urlConnection = new URL(url);
                URLConnection mURLConnection = urlConnection.openConnection();
                HttpURLConnection connection = (HttpURLConnection) mURLConnection;
                connection.setConnectTimeout(15000);
                connection.setReadTimeout(20000);
                connection.setInstanceFollowRedirects(true);
                connection.setUseCaches(false);
                connection.setDefaultUseCaches(false);
                connection.connect();
                connected = connection.getResponseCode() != -1;
                connection.disconnect();
            } catch (Exception var5) {
            }

            isNetwork = connected;
        } else {
            isNetwork = FetchExtensions.isNetworkAvailable(this.context);
        }

        return isNetwork;
    }

    /**
     * NetworkInfoProvider
     *
     * @param context context
     * @param internetCheckUrl internetCheckUrl
     */
    public NetworkInfoProvider(@NotNull Context context, @Nullable String internetCheckUrl) {
        super();
        this.context = context;
        this.internetCheckUrl = internetCheckUrl;
        this.lock = new Object();
        HashSet hashSet = new HashSet();
        this.networkChangeListenerSet = hashSet;
        Object obj = NetManager.getInstance(context);
        if (!(obj instanceof NetManager)) {
            obj = null;
        }
        this.connectivityManager = (NetManager) obj;
        try {
            skills = new MatchingSkills();
            skills.addEvent(CommonEventSupport.COMMON_EVENT_WIFI_CONN_STATE);
            CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(skills);
            networkChangeBroadcastReceiver = new MainEventSubscriber(subscribeInfo);
            CommonEventManager.subscribeCommonEvent(this.networkChangeBroadcastReceiver);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (this.connectivityManager != null) {
            NetSpecifier networkRequest = (new NetSpecifier.Builder()).addBearer(0).addBearer(1).addBearer(3).build();
            NetStatusCallback networkCallback = new NetStatusCallback() {
                public void onLost(@NotNull SystemSettings.Network network) {
                    NetworkInfoProvider.this.notifyNetworkChangeListeners();
                }

                public void onAvailable(@NotNull SystemSettings.Network network) {
                    NetworkInfoProvider.this.notifyNetworkChangeListeners();
                }
            };
            this.networkCallback = networkCallback;
            this.connectivityManager.addNetStatusCallback(networkRequest, networkCallback);
        } else {
            try {
                skills = new MatchingSkills();
                skills.addEvent(CONNECTIVITY_ACTION);
                CommonEventManager.subscribeCommonEvent(this.networkChangeBroadcastReceiver);
                this.broadcastRegistered = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    public interface NetworkChangeListener {
        void onNetworkChanged();
    }

    class MainEventSubscriber extends CommonEventSubscriber {
        MainEventSubscriber(CommonEventSubscribeInfo info) {
            super(info);
        }

        @Override
        public void onReceiveEvent(CommonEventData commonEventData) {
            NetworkInfoProvider.this.notifyNetworkChangeListeners();
        }

    }
}